home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / MoreFiles 1.2.1 / Search.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-04  |  29.7 KB  |  1,021 lines  |  [TEXT/KAHL]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    IndexedSearch and the PBCatSearch compatibility function.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support
  7. **
  8. **    File:        Search.c
  9. **
  10. **    Copyright © 1992-1994 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #ifndef __SEARCH__
  23. #include "Search.h"
  24. #endif
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29. enum
  30. {
  31.     /* Number of LevelRecs to add each time the searchStack is grown */
  32.     /* 20 levels is probably more than reasonable for most volumes. */
  33.     /* If more are needed, they are allocated 20 levels at a time. */
  34.     kAdditionalLevelRecs = 20
  35. };
  36.  
  37. /*****************************************************************************/
  38.  
  39. /*
  40. **    LevelRecs are used to store the directory ID and index whenever
  41. **    IndexedSearch needs to either scan a sub-directory, or return control
  42. **    to the caller because the call has timed out or the number of
  43. **    matches requested has been found. LevelRecs are stored in an array
  44. **    used as a stack.
  45. */
  46. struct    LevelRec
  47. {
  48.     long    dirModDate;    /* for detecting most (but not all) catalog changes */
  49.     long    dirID;
  50.     short    index;
  51. };
  52. typedef struct LevelRec LevelRec;
  53. typedef LevelRec *LevelRecPtr, **LevelRecHandle;
  54.  
  55.  
  56. /*
  57. **    SearchPositionRec is my version of a CatPositionRec. It holds the
  58. **    information I need to resuming searching.
  59. */
  60. #if defined(powerc) || defined (__powerc)
  61. #pragma options align=mac68k
  62. #endif
  63. struct SearchPositionRec
  64. {
  65.     long            initialize;        /* Goofy checksum of volume information used to make */
  66.                                     /* sure we're resuming a search on the same volume. */
  67.     unsigned short    stackDepth;        /* Current depth on searchStack. */
  68.     short            priv[11];        /* For future use... */
  69. };
  70. #if defined(powerc) || defined(__powerc)
  71. #pragma options align=reset
  72. #endif
  73. typedef struct SearchPositionRec SearchPositionRec;
  74. typedef SearchPositionRec *SearchPositionRecPtr;
  75.  
  76.  
  77. /*
  78. **    ExtendedTMTask is a TMTask record extended to hold the timer flag.
  79. */
  80. #if defined(powerc) || defined (__powerc)
  81. #pragma options align=mac68k
  82. #endif
  83. struct ExtendedTMTask
  84. {
  85.     TMTask            theTask;
  86.     Boolean            stopSearch;        /* the Time Mgr task will set stopSearch to */
  87.                                     /* true when the timer expires */
  88. };
  89. #if defined(powerc) || defined(__powerc)
  90. #pragma options align=reset
  91. #endif
  92. typedef struct ExtendedTMTask ExtendedTMTask;
  93. typedef ExtendedTMTask *ExtendedTMTaskPtr;
  94.  
  95. /*****************************************************************************/
  96.  
  97. static    OSErr    CheckVol(StringPtr pathname,
  98.                          short vRefNum,
  99.                          short *realVRefNum,
  100.                          long *volID);
  101.  
  102. static    OSErr    CheckStack(unsigned short stackDepth,
  103.                            LevelRecHandle searchStack,
  104.                            Size *searchStackSize);
  105.  
  106. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  107.                              Boolean *includeFiles,
  108.                              Boolean *includeDirs,
  109.                              Boolean *includeNames);
  110.  
  111. static    Boolean    IsSubString(StringPtr aStringPtr,
  112.                             StringPtr subStringPtr);
  113.  
  114. static    Boolean    CompareMasked(const long *data1,
  115.                               const long *data2,
  116.                               const long *mask,
  117.                               short longsToCompare);
  118.  
  119. static    void    CheckForMatches(CInfoPBPtr cPB,
  120.                                 CSParamPtr userPB,
  121.                                 const Str63 matchName,
  122.                                 Boolean includeFiles,
  123.                                 Boolean includeDirs);
  124.  
  125. #if USESROUTINEDESCRIPTORS
  126.  
  127. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr);
  128.  
  129. #else
  130.  
  131. static    pascal    TMTaskPtr    GetTMTaskPtr(void);
  132.  
  133. static    void    TimeOutTask(void);
  134.  
  135. #endif
  136.  
  137. static    long    GetDirModDate(short vRefNum,
  138.                               long dirID);
  139.  
  140. /*****************************************************************************/
  141.  
  142. /*
  143. **    CheckVol gets the volume's real vRefNum and builds a volID. The volID
  144. **    is used to help insure that calls to resume searching with IndexedSearch
  145. **    are to the same volume as the last call to IndexedSearch.
  146. */
  147. static    OSErr    CheckVol(StringPtr pathname,
  148.                          short vRefNum,
  149.                          short *realVRefNum,
  150.                          long *volID)
  151. {
  152.     HParamBlockRec pb;
  153.     Str255 tempPathname;
  154.     OSErr error;
  155.  
  156.     pb.volumeParam.ioVRefNum = vRefNum;
  157.     if ( pathname == NULL )
  158.     {
  159.         pb.volumeParam.ioNamePtr = NULL;
  160.         pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  161.     }
  162.     else
  163.     {
  164.         BlockMoveData(pathname, tempPathname, pathname[0] + 1);    /* make a copy of the string and */
  165.         pb.volumeParam.ioNamePtr = (StringPtr)tempPathname;    /* use the copy so original isn't trashed */
  166.         pb.volumeParam.ioVolIndex = -1;    /* use ioNamePtr/ioVRefNum combination */
  167.     }
  168.     error = PBHGetVInfoSync(&pb);
  169.     if ( error == noErr )
  170.     {
  171.         /* Return the real vRefNum */
  172.         *realVRefNum = pb.volumeParam.ioVRefNum;
  173.  
  174.         /* Add together a bunch of things that aren't supposed to change on */
  175.         /* a mounted volume that's being searched and that should come up with */
  176.         /* a fairly unique number */
  177.         *volID = pb.volumeParam.ioVCrDate +
  178.                  pb.volumeParam.ioVRefNum +
  179.                  pb.volumeParam.ioVNmAlBlks +
  180.                  pb.volumeParam.ioVAlBlkSiz +
  181.                  pb.volumeParam.ioVFSID;
  182.     }
  183.     return ( error );
  184. }
  185.  
  186. /*****************************************************************************/
  187.  
  188. /*
  189. **    CheckStack checks the size of the search stack (array) to see if there's
  190. **    room to push another LevelRec. If not, CheckStack grows the stack by
  191. **    another kAdditionalLevelRecs elements.
  192. */
  193. static    OSErr    CheckStack(unsigned short stackDepth,
  194.                            LevelRecHandle searchStack,
  195.                            Size *searchStackSize)
  196. {
  197.     OSErr    result;
  198.     
  199.     if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
  200.     {
  201.         /* Time to grow stack */
  202.         SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
  203.         result = MemError();    /* should be noErr */
  204.         *searchStackSize = GetHandleSize((Handle)searchStack);
  205.     }
  206.     else
  207.     {
  208.         result = noErr;
  209.     }
  210.     
  211.     return ( result );
  212. }
  213.  
  214. /*****************************************************************************/
  215.  
  216. /*
  217. **    VerifyUserPB makes sure the parameter block passed to IndexedSearch has
  218. **    valid parameters. By making this check once, we don't have to worry about
  219. **    things like NULL pointers, strings being too long, etc.
  220. **    VerifyUserPB also determines if the search includes files and/or
  221. **    directories, and determines if a full or partial name search was requested.
  222. */
  223. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  224.                              Boolean *includeFiles,
  225.                              Boolean *includeDirs,
  226.                              Boolean *includeNames)
  227. {
  228.     CInfoPBPtr    searchInfo1;
  229.     CInfoPBPtr    searchInfo2;
  230.     
  231.     searchInfo1 = userPB->ioSearchInfo1;
  232.     searchInfo2 = userPB->ioSearchInfo2;
  233.     
  234.     /* ioMatchPtr cannot be NULL */
  235.     if ( userPB->ioMatchPtr == NULL )
  236.         goto ParamErrExit;
  237.     
  238.     /* ioSearchInfo1 cannot be NULL */
  239.     if ( searchInfo1 == NULL )
  240.         goto ParamErrExit;
  241.     
  242.     /* If any bits except partialName, fullName, or negate are set, then */
  243.     /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required  */
  244.     if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) &&
  245.          ( searchInfo2 == NULL ))
  246.         goto ParamErrExit;
  247.     
  248.     *includeFiles = false;
  249.     *includeDirs = false;
  250.     *includeNames = false;
  251.     
  252.     if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
  253.     {
  254.         /* If any kind of name matching is requested, then ioNamePtr in */
  255.         /* ioSearchInfo1 cannot be NULL or a zero-length string */
  256.         if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) ||
  257.              (searchInfo1->hFileInfo.ioNamePtr[0] == 0) ||
  258.              (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) )
  259.             goto ParamErrExit;
  260.         
  261.         *includeNames = true;
  262.     }
  263.     
  264.     if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
  265.     {
  266.         /* The only attributes you can search on are the directory flag */
  267.         /* and the locked flag. */
  268.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(ioDirMask | 0x01)) != 0 )
  269.             goto ParamErrExit;
  270.         
  271.         /* interested in the directory bit? */
  272.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  273.         {
  274.             /* yes, so do they want just directories or just files? */
  275.             if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  276.                 *includeDirs = true;
  277.             else
  278.                 *includeFiles = true;
  279.         }
  280.         else
  281.         {
  282.             /* no interest in directory bit - get both files and directories */
  283.             *includeDirs = true;
  284.             *includeFiles = true;
  285.         }
  286.     }
  287.     else
  288.     {
  289.         /* no attribute checking - get both files and directories */
  290.         *includeDirs = true;
  291.         *includeFiles = true;
  292.     }
  293.     
  294.     /* If directories are included in the search, */
  295.     /* then the locked attribute cannot be requested. */
  296.     if ( *includeDirs &&
  297.          ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
  298.          ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) )
  299.         goto ParamErrExit;
  300.     
  301.     /* If files are included in the search, then there cannot be */
  302.     /* a search on the number of files. */
  303.     if ( *includeFiles &&
  304.          ((userPB->ioSearchBits & fsSBDrNmFls) != 0) )
  305.         goto ParamErrExit;
  306.     
  307.     /* If directories are included in the search, then there cannot */
  308.     /* be a search on file lengths. */
  309.     if ( *includeDirs &&
  310.          ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
  311.         goto ParamErrExit;
  312.     
  313.     return ( noErr );
  314.          
  315. ParamErrExit:
  316.     return ( paramErr );
  317. }
  318.  
  319. /*****************************************************************************/
  320.  
  321. /*
  322. **    IsSubString checks to see if a string is a substring of another string.
  323. **    Both input strings have already been converted to all uppercase using
  324. **    UprString (the same non-international call the File Manager uses).
  325. */
  326. static    Boolean    IsSubString(StringPtr aStringPtr,
  327.                             StringPtr subStringPtr)
  328. {
  329.     /* Put variables used within the loop into registers if possible */
  330.     register short    strIndex;        /* running index into string */
  331.     register short    subStrIndex;    /* running index into subString */
  332.     register short    count;            /* search counter */
  333.     register short    index;            /* current index into string */
  334.     register short    subStrLength;    /* length of subString */
  335.     short            strLength;        /* length of string */
  336.     Boolean            found;            /* result of test */
  337.  
  338.     found = false;
  339.     strLength = aStringPtr[0];
  340.     subStrLength = subStringPtr[0];
  341.         
  342.     if ( subStrLength <= strLength)
  343.     {
  344.         index = 1;            /* start looking at first character */
  345.         count = strLength - subStrLength + 1;    /* continue looking until remaining string */
  346.                                                 /* is shorter than substring */
  347.         strIndex = index;    /* start string index at index */
  348.         subStrIndex = 1;    /* start subString index at 1 */
  349.         do
  350.         {
  351.             if ( aStringPtr[strIndex] == subStringPtr[subStrIndex] )
  352.             {
  353.                 if ( subStrIndex == subStrLength )
  354.                 {
  355.                     /* all characters in subString were found */
  356.                     found = true;
  357.                 }
  358.                 else
  359.                 {
  360.                     /* check next character of substring against next character of string */
  361.                     ++subStrIndex;
  362.                     ++strIndex;
  363.                 }
  364.             }
  365.             else
  366.             {
  367.                 /* start substring search again at next string character */
  368.                 ++index;
  369.                 --count;
  370.             }
  371.         } while ( (count != 0) && (!found) );
  372.     }
  373.     
  374.     return ( found );
  375. }
  376.  
  377. /*****************************************************************************/
  378.  
  379. /*
  380. **    CompareMasked does a bitwise comparison with mask on 1 or more longs.
  381. **    data1 and data2 are first exclusive-ORed together resulting with bits set
  382. **    where they are different. That value is then ANDed with the mask resulting
  383. **    with bits set if the test fails. true is returned if the tests pass.
  384. */
  385. static    Boolean    CompareMasked(const long *data1,
  386.                               const long *data2,
  387.                               const long *mask,
  388.                               short longsToCompare)
  389. {
  390.     Boolean    result = true;
  391.     
  392.     while ( (longsToCompare != 0) && (result == true) )
  393.     {
  394.         /* (*data1 ^ *data2) = bits that are different, so... */
  395.         /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
  396.         
  397.         if ( ((*data1 ^ *data2) & *mask) != 0 )
  398.             result = false;
  399.         
  400.         ++data1;
  401.         ++data2;
  402.         ++mask;
  403.         --longsToCompare;
  404.     }
  405.     
  406.     return ( result );
  407. }
  408.  
  409. /*****************************************************************************/
  410.  
  411. /*
  412. **    Check for matches compares the search criteria in userPB to the file
  413. **    system object in cPB. If there's a match, then the information in cPB is
  414. **    is added to the match array and the actual match count is incremented.
  415. */
  416. static    void    CheckForMatches(CInfoPBPtr cPB,
  417.                                 CSParamPtr userPB,
  418.                                 const Str63 matchName,
  419.                                 Boolean includeFiles,
  420.                                 Boolean includeDirs)
  421. {
  422.     long        searchBits;
  423.     CInfoPBPtr    searchInfo1;
  424.     CInfoPBPtr    searchInfo2;
  425.     Str63        itemName;        /* copy of object's name for partial name matching */
  426.     Boolean        foundMatch;
  427.     
  428.     foundMatch = false;            /* default to no match */
  429.     
  430.     searchBits = userPB->ioSearchBits;
  431.     searchInfo1 = userPB->ioSearchInfo1;
  432.     searchInfo2 = userPB->ioSearchInfo2;
  433.     
  434.     /* Into the if statements that go on forever... */
  435.     
  436.     if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 )
  437.     {
  438.         if (!includeFiles)
  439.             goto Failed;
  440.     }
  441.     else
  442.     {
  443.         if (!includeDirs)
  444.             goto Failed;
  445.     }
  446.     
  447.     if ( (searchBits & fsSBPartialName) != 0 )
  448.     {
  449.         if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
  450.              (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
  451.         {
  452.             /* Make uppercase copy of object name */
  453.             BlockMoveData(cPB->hFileInfo.ioNamePtr,
  454.                             itemName,
  455.                             cPB->hFileInfo.ioNamePtr[0] + 1);
  456.             /* Use the same non-international call the File Manager uses */
  457.             UprString(itemName, true);
  458.         }
  459.         else
  460.         {
  461.             goto Failed;
  462.         }
  463.         
  464.         {
  465.             if ( !IsSubString(itemName, (StringPtr)matchName) )
  466.             {
  467.                 goto Failed;
  468.             }
  469.             else if ( searchBits == fsSBPartialName )
  470.             {
  471.                 /* optimize for name matching only since it is most common way to search */
  472.                 goto Hit;
  473.             }
  474.         }
  475.     }
  476.     
  477.     if ( (searchBits & fsSBFullName) != 0 )
  478.     {
  479.         /* Use the same non-international call the File Manager uses */
  480.         if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
  481.         {
  482.             goto Failed;
  483.         }
  484.         else if ( searchBits == fsSBFullName )
  485.         {
  486.             /* optimize for name matching only since it is most common way to search */
  487.             goto Hit;
  488.         }
  489.     }
  490.     
  491.     if ( (searchBits & fsSBFlParID) != 0 )
  492.     {
  493.         if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
  494.              ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
  495.         {
  496.             goto Failed;
  497.         }
  498.     }
  499.     
  500.     if ( (searchBits & fsSBFlAttrib) != 0 )
  501.     {
  502.         if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
  503.               searchInfo2->hFileInfo.ioFlAttrib) != 0 )
  504.         {
  505.             goto Failed;
  506.         }
  507.     }
  508.     
  509.     if ( (searchBits & fsSBDrNmFls) != 0 )
  510.     {
  511.         if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
  512.              ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
  513.         {
  514.             goto Failed;
  515.         }
  516.     }
  517.  
  518.     if ( (searchBits & fsSBFlFndrInfo) != 0 )    /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
  519.     {
  520.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
  521.                             (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
  522.                             (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
  523.                             sizeof(FInfo) / sizeof(long)) )
  524.         {
  525.             goto Failed;
  526.         }
  527.     }
  528.     
  529.     if ( (searchBits & fsSBFlXFndrInfo) != 0 )    /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
  530.     {
  531.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
  532.                             (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
  533.                             (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
  534.                             sizeof(FXInfo) / sizeof(long)) )
  535.         {
  536.             goto Failed;
  537.         }
  538.     }
  539.     
  540.     if ( (searchBits & fsSBFlLgLen) != 0 )
  541.     {
  542.         if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
  543.              ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
  544.         {
  545.             goto Failed;
  546.         }
  547.     }
  548.  
  549.     if ( (searchBits & fsSBFlPyLen) != 0 )
  550.     {
  551.         if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
  552.              ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
  553.         {
  554.             goto Failed;
  555.         }
  556.     }
  557.  
  558.     if ( (searchBits & fsSBFlRLgLen) != 0 )
  559.     {
  560.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
  561.              ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
  562.         {
  563.             goto Failed;
  564.         }
  565.     }
  566.  
  567.     if ( (searchBits & fsSBFlRPyLen) != 0 )
  568.     {
  569.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
  570.              ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
  571.         {
  572.             goto Failed;
  573.         }
  574.     }
  575.  
  576.     if ( (searchBits & fsSBFlCrDat) != 0 )    /* fsSBFlCrDat is same as fsSBDrCrDat */
  577.     {
  578.         if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
  579.              ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
  580.         {
  581.             goto Failed;
  582.         }
  583.     }
  584.  
  585.     if ( (searchBits & fsSBFlMdDat) != 0 )    /* fsSBFlMdDat is same as fsSBDrMdDat */
  586.     {
  587.         if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
  588.              ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
  589.         {
  590.             goto Failed;
  591.         }
  592.     }
  593.  
  594.     if ( (searchBits & fsSBFlBkDat) != 0 )    /* fsSBFlBkDat is same as fsSBDrBkDat */
  595.     {
  596.         if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
  597.              ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
  598.         {
  599.             goto Failed;
  600.         }
  601.     }
  602.  
  603.     /* Hey, we passed all of the tests! */
  604.     
  605. Hit:
  606.     foundMatch = true;
  607.  
  608. /* foundMatch is false if code jumps to Failed */
  609. Failed:
  610.     /* Do we reverse our findings? */
  611.     if ( (searchBits & fsSBNegate) != 0 )
  612.         foundMatch = !foundMatch;    /* matches are not, not matches are */
  613.     
  614.     if ( foundMatch )
  615.     {
  616.  
  617.         /* Move the match into the match buffer */
  618.         userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum;
  619.         userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID;
  620.         if ( cPB->hFileInfo.ioNamePtr[0] > 63 )
  621.             cPB->hFileInfo.ioNamePtr[0] = 63;
  622.         BlockMoveData(cPB->hFileInfo.ioNamePtr,
  623.                       userPB->ioMatchPtr[userPB->ioActMatchCount].name,
  624.                       cPB->hFileInfo.ioNamePtr[0] + 1);
  625.         
  626.         /* increment the actual count */
  627.         ++(userPB->ioActMatchCount);
  628.     }
  629. }
  630.  
  631. /*****************************************************************************/
  632.  
  633. /*
  634. **    TimeOutTask is executed when the timer goes off. It simply sets the
  635. **    stopSearch field to true. After each object is found and possibly added
  636. **    to the matches buffer, stopSearch is checked to see if the search should
  637. **    continue.
  638. */
  639.  
  640. #if USESROUTINEDESCRIPTORS
  641.  
  642. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr)
  643. {
  644.     ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
  645. }
  646.  
  647. #else
  648.  
  649. static    pascal    TMTaskPtr    GetTMTaskPtr(void)
  650.     ONEWORDINLINE(0x2e89);    /* MOVE.L A1,(SP) */
  651.     
  652. static    void    TimeOutTask(void)
  653. {
  654.     ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
  655. }
  656.  
  657. #endif
  658.  
  659. /*****************************************************************************/
  660.  
  661. /*
  662. **    GetDirModDate returns the modification date of a directory. If there is
  663. **    an error getting the modification date, -1 is returned to indicate
  664. **    something went wrong.
  665. */
  666. static    long    GetDirModDate(short vRefNum,
  667.                               long dirID)
  668. {
  669.     CInfoPBRec pb;
  670.     OSErr modDate;
  671.  
  672.     pb.dirInfo.ioNamePtr = NULL;
  673.     pb.dirInfo.ioVRefNum = vRefNum;
  674.     pb.dirInfo.ioDrDirID = dirID;
  675.     pb.dirInfo.ioFDirIndex = -1;    /* use ioDrDirID */
  676.     if ( PBGetCatInfoSync(&pb) == noErr )
  677.     {
  678.         modDate = pb.dirInfo.ioDrMdDat;
  679.     }
  680.     else
  681.     {
  682.         modDate = -1;
  683.     }
  684.     
  685.     return ( modDate );
  686. }
  687. /*****************************************************************************/
  688.  
  689. pascal    OSErr    IndexedSearch(CSParamPtr pb,
  690.                               long dirID)
  691. {
  692.     static LevelRecHandle    searchStack = NULL;        /* static handle to LevelRec stack */
  693.     static Size                searchStackSize = 0;    /* size of static handle */
  694.     SearchPositionRecPtr    catPosition;
  695.     long                    modDate;
  696.     short                    index;
  697.     ExtendedTMTask            timerTask;
  698.     OSErr                    result;
  699.     short                    realVRefNum;
  700.     Str63                    itemName;
  701.     CInfoPBRec                cPB;
  702.     long                    tempLong;
  703.     Boolean                    includeFiles;
  704.     Boolean                    includeDirs;
  705.     Boolean                    includeNames;
  706.     Str63                    upperName;
  707.     
  708.     timerTask.stopSearch = false;    /* don't stop yet! */
  709.     
  710.     /* If request has a timeout, install a Time Manager task. */
  711.     if ( pb->ioSearchTime != 0 )
  712.     {
  713.         /* Start timer */
  714.         timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
  715.         InsTime((QElemPtr)&(timerTask.theTask));
  716.         PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
  717.     }
  718.     
  719.     /* Check the parameter block passed for things that we don't want to assume */
  720.     /* are OK later in the code. For example, make sure pointers to data structures */
  721.     /* and buffers are not NULL.  And while we're in there, see if the request */
  722.     /* specified searching for files, directories, or both, and see if the search */
  723.     /* was by full or partial name. */
  724.     result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames);
  725.     if ( result == noErr )
  726.     {
  727.         pb->ioActMatchCount = 0;    /* no matches yet */
  728.     
  729.         if ( includeNames )
  730.         {
  731.             /* The search includes seach by full or partial name. */
  732.             /* Make an upper case copy of the match string to pass to */
  733.             /* CheckForMatches. */
  734.             BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr,
  735.                             upperName,
  736.                             pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
  737.             /* Use the same non-international call the File Manager uses */
  738.             UprString(upperName, true);
  739.         }
  740.         
  741.         /* Prevent casting to my type throughout code */
  742.         catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
  743.         
  744.         /* Create searchStack first time called */
  745.         if ( searchStack == NULL )
  746.         {
  747.             searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
  748.         }
  749.         
  750.         /* Make sure searchStack really exists */
  751.         if ( searchStack != NULL )
  752.         {
  753.             searchStackSize = GetHandleSize((Handle)searchStack);
  754.             
  755.             /* See if the search is a new search or a resumed search. */
  756.             if ( catPosition->initialize == 0 )
  757.             {
  758.                 /* New search. */
  759.                 
  760.                 /* Get the real vRefNum and fill in catPosition->initialize. */ 
  761.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
  762.                 if ( result == noErr )
  763.                 {
  764.                     /* clear searchStack */
  765.                     catPosition->stackDepth = 0;
  766.                     
  767.                     /* use dirID parameter passed and... */
  768.                     index = -1;    /* start with the passed directory itself! */
  769.                 }
  770.             }
  771.             else
  772.             {
  773.                 /* We're resuming a search. */
  774.     
  775.                 /* Get the real vRefNum and make sure catPosition->initialize is valid. */ 
  776.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong);
  777.                 if ( result == noErr )
  778.                 {
  779.                     /* Make sure the resumed search is to the same volume! */
  780.                     if ( catPosition->initialize == tempLong )
  781.                     {
  782.                         /* For resume, catPosition->stackDepth > 0 */
  783.                         if ( catPosition->stackDepth > 0 )
  784.                         {
  785.                             /* Position catPosition->stackDepth to access last saved level */
  786.                             --(catPosition->stackDepth);
  787.             
  788.                             /* Get the dirID and index for the next item */
  789.                             dirID = (*searchStack)[catPosition->stackDepth].dirID;
  790.                             index = (*searchStack)[catPosition->stackDepth].index;
  791.                             
  792.                             /* Check the dir's mod date against the saved mode date on our "stack" */
  793.                             modDate = GetDirModDate(realVRefNum, dirID);
  794.                             if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  795.                                 result = catChangedErr;
  796.                         }
  797.                         else
  798.                         {
  799.                             /* Invalid catPosition record was passed */
  800.                             result = paramErr;
  801.                         }
  802.                     }
  803.                     else
  804.                     {
  805.                         /* The volume is not the same */
  806.                         result = catChangedErr;
  807.                     }
  808.                 }
  809.             }
  810.             
  811.             if ( result == noErr )
  812.             {
  813.                 /* ioNamePtr and ioVRefNum only need to be set up once. */
  814.                 cPB.hFileInfo.ioNamePtr = itemName;
  815.                 cPB.hFileInfo.ioVRefNum = realVRefNum;
  816.                 
  817.                 /*
  818.                 **    Here's the loop that:
  819.                 **        Finds the next item on the volume.
  820.                 **        If noErr, calls the code to check for matches and add matches
  821.                 **            to the match buffer.
  822.                 **        Sets up dirID and index for to find the next item on the volume.
  823.                 **
  824.                 **    The looping ends when:
  825.                 **        a) an unexpected error is returned by PBGetCatInfo. All that
  826.                 **            is expected is noErr and fnfErr (after the last item in a
  827.                 **            directory is found).
  828.                 **        b) the caller specified a timeout and our Time Manager task
  829.                 **            has fired.
  830.                 **        c) the number of matches requested by the caller has been found.
  831.                 **        d) the last item on the volume was found.
  832.                 */
  833.                 do
  834.                 {
  835.                     /* get the next item */
  836.                     cPB.hFileInfo.ioFDirIndex = index;
  837.                     cPB.hFileInfo.ioDirID = dirID;
  838.                     result = PBGetCatInfoSync(&cPB);
  839.                     if ( index != -1 )
  840.                     {
  841.                         if ( result == noErr )
  842.                         {
  843.                             /* We found something */
  844.         
  845.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  846.                             
  847.                             ++index;
  848.                             if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  849.                             {
  850.                                 /* It's a directory */
  851.                                 
  852.                                 result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  853.                                 if ( result == noErr )
  854.                                 {
  855.                                     /* Save the current state on the searchStack */
  856.                                     /* when we come back, this is where we'll start */
  857.                                     (*searchStack)[catPosition->stackDepth].dirID = dirID;
  858.                                     (*searchStack)[catPosition->stackDepth].index = index;
  859.                                     (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  860.                                     
  861.                                     /* position catPosition->stackDepth for next saved level */
  862.                                     ++(catPosition->stackDepth);
  863.                                     
  864.                                     /* The next item to get is the 1st item in the child directory */
  865.                                     dirID = cPB.dirInfo.ioDrDirID;
  866.                                     index = 1;
  867.                                 }
  868.                             }
  869.                             /* else do nothing for files */
  870.                         }
  871.                         else if ( result == fnfErr )
  872.                         {
  873.                             /* End of directory found. */
  874.                             /* Restore last thing put on stack and */
  875.                             /* see if we need to continue or quit. */
  876.                             if ( catPosition->stackDepth > 0 )
  877.                             {
  878.                                 /* position catPosition->stackDepth to access last saved level */
  879.                                 --(catPosition->stackDepth);
  880.                                 
  881.                                 dirID = (*searchStack)[catPosition->stackDepth].dirID;
  882.                                 index = (*searchStack)[catPosition->stackDepth].index;
  883.                                 
  884.                                 /* Check the dir's mod date against the saved mode date on our "stack" */
  885.                                 modDate = GetDirModDate(realVRefNum, dirID);
  886.                                 if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  887.                                     result = catChangedErr;
  888.     
  889.                                 /* Going back to ancestor directory. */
  890.                                 /* Clear error so we can continue. */
  891.                                 result = noErr;
  892.                             }
  893.                             else
  894.                             {
  895.                                 /* We hit the bottom of the stack, so we'll let the */
  896.                                 /* the eofErr drop us out of the loop. */
  897.                                 result = eofErr;
  898.                             }
  899.                         }
  900.                     }
  901.                     else
  902.                     {
  903.                         /* Special case for index == -1; that means that we're starting */
  904.                         /* a new search and so the first item to check is the directory */
  905.                         /* passed to us. */
  906.                         if ( result == noErr )
  907.                         {
  908.                             /* We found something */
  909.         
  910.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  911.                             
  912.                             /* Now, set the index to 1 and then we're ready to look inside */
  913.                             /* the passed directory. */
  914.                             index = 1;
  915.                         }
  916.                     }
  917.                 } while ( (!timerTask.stopSearch) &&    /* timer hasn't fired */
  918.                           (result == noErr) &&            /* no unexpected errors */
  919.                           (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */
  920.                 
  921.                 /* Did we drop out of the loop because of timeout or */
  922.                 /* ioReqMatchCount was found? */
  923.                 if ( result == noErr )
  924.                 {
  925.                     result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  926.                     if ( result == noErr )
  927.                     {
  928.                         /* Either there was a timeout or ioReqMatchCount was reached. */
  929.                         /* Save the dirID and index for the next time we're called. */
  930.                         
  931.                         (*searchStack)[catPosition->stackDepth].dirID = dirID;
  932.                         (*searchStack)[catPosition->stackDepth].index = index;
  933.                         (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  934.                         
  935.                         /* position catPosition->stackDepth for next saved level */
  936.                         
  937.                         ++(catPosition->stackDepth);
  938.                     }
  939.                 }
  940.             }
  941.         }
  942.         else
  943.         {
  944.             /* searchStack Handle could not be allocated */
  945.             result = memFullErr;
  946.         }
  947.     }
  948.     
  949.     if ( pb->ioSearchTime != 0 )
  950.     {
  951.         /* Stop Time Manager task here if it was installed */
  952.         RmvTime((QElemPtr)&(timerTask.theTask));
  953.         DisposeRoutineDescriptor(timerTask.theTask.tmAddr);
  954.     }
  955.     
  956.     return ( result );
  957. }
  958.  
  959. /*****************************************************************************/
  960.  
  961. pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
  962. {
  963.     static Boolean            fullExtFSDispatchingtested = false;
  964.     static Boolean            hasFullExtFSDispatching = false;
  965.     OSErr                     result;
  966.     Boolean                    supportsCatSearch;
  967.     long                    response;
  968.     GetVolParmsInfoBuffer    volParmsInfo;
  969.     long                    infoSize;
  970.     
  971.     result = noErr;
  972.  
  973.     /* See if File Manager will pass CatSearch requests to external file systems */
  974.     /* we'll store the results in a static variable so we don't have to call Gestalt */
  975.     /* everytime we're called. */
  976.     if ( !fullExtFSDispatchingtested )
  977.     {
  978.         fullExtFSDispatchingtested = true;
  979.         if ( Gestalt(gestaltFSAttr, &response) == noErr )
  980.         {
  981.             hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
  982.         }
  983.     }
  984.     
  985.     /* CatSearch is a per volume attribute, so we have to check each time we're */
  986.     /* called to see if it is available on the volume specified. */
  987.     supportsCatSearch = false;
  988.     if ( hasFullExtFSDispatching )
  989.     {
  990.         infoSize = sizeof(GetVolParmsInfoBuffer);
  991.         result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
  992.                             &volParmsInfo, &infoSize);
  993.         if ( result == noErr )
  994.         {
  995.             supportsCatSearch = hasCatSearch(volParmsInfo);
  996.         }
  997.     }
  998.     
  999.     /* noErr or paramErr is OK here. */
  1000.     /* paramErr just means that GetVolParms isn't supported by this volume */
  1001.     if ( (result == noErr) || (result == paramErr) )
  1002.     {
  1003.         if ( supportsCatSearch )
  1004.         {
  1005.             /* Volume supports CatSearch so use it. */
  1006.             /* CatSearch is faster than an indexed search. */
  1007.             result = PBCatSearchSync(paramBlock);
  1008.         }
  1009.         else
  1010.         {
  1011.             /* Volume doesn't support CatSearch so */
  1012.             /* search using IndexedSearch from root directory. */
  1013.             result = IndexedSearch(paramBlock, fsRtDirID);
  1014.         }
  1015.     }
  1016.     
  1017.     return ( result );
  1018. }
  1019.  
  1020. /*****************************************************************************/
  1021.